/**
 * @author YJSYS
 *
 */

package com.qd.extcamclient;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;

public class ClientAsynkTask extends AsyncTask<Void, String, String> {
	private MainActivity activity;
	private static final int SOCKET_TIMEOUT = 5000;

	private byte[] queue;
	private int qIndex;

	private FileOutputStream fos;

	private static final int PRT_POS_DATA = 7;
	private static final int PRT_POS_COMMAND = 2;
	private static final int PRT_POS_RESULT = 1;
	private static final int PRT_POS_LEN = 3;

	/** 서버 접속을 위한 소켓을 생성하고 서버와 통신 작업을 한다.
	 * @param context
	 * @param statusText
	 */
	public ClientAsynkTask(MainActivity activity) {
		this.activity = activity;
		this.queue = new byte[1024000];
		this.qIndex = 0;
	}

	/**
	 * 서버로 부터 받은 데이터를 처리한다.
	 */
	private void doResponse() {
		byte[] dataBuf = null;
		int nDataLen = 0;
		for (;;) {
			if (qIndex <= PRT_POS_DATA)
				break;

			int nPackLen = (queue[PRT_POS_LEN] << 24)
					+ ((queue[PRT_POS_LEN + 1] & 0xff) << 16)
					+ ((queue[PRT_POS_LEN + 2] & 0xff) << 8)
					+ (queue[PRT_POS_LEN + 3] & 0xFF);
			nPackLen += 8;
			if (nPackLen > qIndex)
				break;

			Command cmd = Command.values()[queue[PRT_POS_COMMAND]];
			if (queue[PRT_POS_DATA] != (byte) 0xf2) {
				nDataLen =  (nPackLen - 8);
				dataBuf = new byte[nDataLen];
				System.arraycopy(queue, PRT_POS_DATA, dataBuf, 0, nDataLen);
			}

			removeQueue(nPackLen);

			/* error check */
			if (1 == queue[PRT_POS_RESULT]) {
				showMessage("error");
				break;
			}

			switch (cmd) {
			case START_RECORD:
				this.showMessage("recording strated ....");
				break;
			case STOP_RECORD:
				this.showMessage("recording stoped ....");
				break;
			case GET_FILE_LIST:
				this.showMessage(new String(dataBuf));
				break;
			case DELETE_FILE:
				this.showMessage(new String(dataBuf));
				break;
			case GET_FILE:
				this.fileDownload(dataBuf);
				break;
			case GET_DATA:
				this.showMessage(new String(dataBuf));
				break;
			case LOGOUT:
				this.showMessage("Logout Comppleted..");
				this.activity.logOut();
				break;
			}
		}
	}

	/** 파일을 다운로드 한다.
	 * @param data 파일 데이터
	 */
	private void fileDownload(byte[] data) {
		if (data == null) {
			this.showMessage("Error....File Downloading");
		}

		switch (data[0]) {
		case 1: // header, file create
			byte[] fileNameByte = new byte[data.length - 1];
			System.arraycopy(data, 1, fileNameByte, 0, data.length - 1);
			String fileName = new String(fileNameByte);
			File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), fileName);
			try {
				fos = new FileOutputStream(file);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
			this.showMessage("File Downloading Begin");
			break;
		case 2: // file data
			if (fos != null) {
				try {
					fos.write(data, 1, data.length - 1);
				} catch (IOException e) {
					e.printStackTrace();
				}
				this.showMessage("File Downloading.......");
			}
			break;
		case 3: // tail
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				fos = null;
				this.showMessage("File Downloading End");
			}
			break;
		}

		this.showMessage(new String(data));
	}

	/* (non-Javadoc)
	 * @see android.os.AsyncTask#onProgressUpdate(Progress[])
	 */
	@Override
	protected void onProgressUpdate(String... params) {
		activity.setText(params[0]);
	}

	/* (non-Javadoc)
	 * @see android.os.AsyncTask#doInBackground(Params[])
	 */
	@Override
	protected String doInBackground(Void... params) {
		activity.socket = new Socket();
		int port = MainActivity.SERVER_PORT;

		try {
			Log.e(MainActivity.TAG, "Opening client socket - ");
			activity.socket.bind(null);
			activity.socket.connect((new InetSocketAddress(InetAddress.getByName(activity.getHostAddr()), port)), SOCKET_TIMEOUT);
			Log.e(MainActivity.TAG, "connected... ");
			showMessage("connected... ");
			InputStream is = activity.socket.getInputStream();
			byte[] buffer = new byte[256];
			int receivedLen = 0;
			while (activity.running) {
				receivedLen = is.read(buffer);
				if (receivedLen > 0) {
					// System.arraycopy(buffer,0,this.queue, this.qIndex, receivedLen);
					addQueue(buffer, receivedLen);
					doResponse();
				}
			}
		} catch (IllegalArgumentException e) {
			Log.e(MainActivity.TAG, e.getMessage());
		} catch (IOException e) {
			Log.e(MainActivity.TAG, e.getMessage());
		} catch (Exception e) {
			Log.e(MainActivity.TAG, e.getMessage());
		} finally {
			if (activity.socket != null) {
				if (activity.socket.isConnected()) {
					try {
						activity.socket.close();
					} catch (IOException e) {
						// Give up
						e.printStackTrace();
					}
				}
			}
		}

		return null;
	}

	/** 메세지를 화면에 표시한다.
	 * @param message
	 */
	private void showMessage(String message) {
		this.publishProgress(message);
	}

	/** 서버로 부터 받은 데이터를 MessageQueue에 추가한다.
	 * @param buffer 데이터
	 * @param nRcvdLen 데이터 길이
	 */
	private void addQueue(byte[] buffer, int nRcvdLen) {
		System.arraycopy(buffer, 0, queue, qIndex, nRcvdLen);
		qIndex += nRcvdLen;
	}

	/** MessageQueue에서 서버 데이터를 삭제한다.
	 * @param nPackLen 서버데이터 길이
	 */
	private void removeQueue(int nPackLen) {
		System.arraycopy(queue, nPackLen, queue, 0, qIndex - nPackLen);
		qIndex -= nPackLen;
	}
}
